﻿using Benefactor.Models.DbModels;
using Benefactor.Services.Repositories;
using Bigway.Common.Extensions;
using Bigway.Common.Utils;

namespace Benefactor.Services;

public class BenefitsCardService(
    IBenefitRepository benefitRepository,
    IVoucherRepository voucherRepository,
    ICardNoGenerator cardNoGenerator,
    ILogger<BenefitsCardService> logger) : IBenefitsCardService
{
    private const int RandomCodeLength = 4;
    private const string RandomCodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    public async Task<List<Voucher>> GenerateVouchersAsync(
        BenefitsCard benefitsCard,
        IssuedBenefitsCard issuedBenefitsCard,
        DateOnly? voucherValidFrom,
        DateOnly? voucherValidTo,
        CancellationToken token = default)
    {
        logger.LogInformation("Generating vouchers for the issued benefits card {IssuedBenefitsCardId}", issuedBenefitsCard.Id);
        var voucherValidFromValue = voucherValidFrom ?? issuedBenefitsCard.ValidFrom;
        var voucherValidToValue = voucherValidTo?? issuedBenefitsCard.ValidTo;

        var today = DateOnlyExtensions.Today();
        var durationInDays = voucherValidToValue.GetDurationInDays(voucherValidFromValue);
        if (voucherValidFromValue < today)
        {
            voucherValidFromValue = today;
            voucherValidToValue = today.AddDays(durationInDays - 1);
        }

        var benefits = await benefitRepository.GetAllAsync(
            query =>
                query.Where(x => !x.IsDeleted && 
                    x.BenefitsCardId == issuedBenefitsCard.BenefitsCardId
                ).Includes(x => x.Product),
            token);

        var cardNo = issuedBenefitsCard.CardNo;
        var systemId = issuedBenefitsCard.SystemId;
        var userId = issuedBenefitsCard.UserId;
        var districtId = benefitsCard.DistrictId;
        var allVouchers = benefits.SelectMany(benefit =>
            {
                var product = benefit.Product!;
                var consumeCodes = RandomCodeGenerator.Generate(RandomCodeLength, benefit.Count, RandomCodeChars);
                var vouchers = consumeCodes.Select(code =>
                    new Voucher
                    {
                        SystemId = systemId,
                        BenefitId = benefit.Id,
                        MerchantId = product.MerchantId,
                        ProductId = product.Id,
                        UserId = userId,
                        Name = product.Name,
                        BenefitsCardId = benefitsCard.Id,
                        IssuedBenefitsCardId = issuedBenefitsCard.Id,
                        IssuedBenefitsCardNo = cardNo,
                        ConsumeCode = benefitsCard.DistrictPrefix + code,
                        DistrictId = districtId,
                        ValidFrom = voucherValidFromValue,
                        ValidTo = voucherValidToValue
                    });
                return vouchers;
            }).ToList();

        await voucherRepository.AddAllAsync(allVouchers, token);
        return allVouchers;
    }

    public async Task<IssuedBenefitsCard> IssueAsync(BenefitsCard benefitsCard,
        string userId,
        DateOnly validFrom,
        DateOnly validTo,
        CancellationToken token = default)
    {
        var systemId = benefitsCard.SystemId;

        var issuedBenefitsCard = new IssuedBenefitsCard
        {
            SystemId = systemId,
            BenefitsCardId = benefitsCard.Id,
            CardNo = await cardNoGenerator.GenerateAsync(systemId, benefitsCard.Id, token: token),
            ValidFrom = validFrom,
            ValidTo = validTo,
            UserId = userId,
            IssueTime = DateTime.UtcNow
        };

        return issuedBenefitsCard;
    }
}